package net.interest.curation.service;

import java.io.IOException;
import java.io.OutputStreamWriter;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import net.interest.curation.meta.TwitterUserMeta;
import net.interest.curation.model.SocialUser;
import net.interest.curation.model.TwitterList;
import net.interest.curation.model.TwitterUser;
import net.interest.curation.model.User;

import org.slim3.datastore.Datastore;

import twitter4j.PagableResponseList;
import twitter4j.Paging;
import twitter4j.ResponseList;
import twitter4j.Status;
import twitter4j.Twitter;
import twitter4j.TwitterException;
import twitter4j.TwitterFactory;
import twitter4j.UserList;
import twitter4j.auth.AccessToken;
import twitter4j.auth.RequestToken;

import com.google.appengine.api.datastore.Key;
import com.google.gson.stream.JsonWriter;

public class TwtService implements SocialService{

    /**
     * Twitter認証画面へリダイレクトするURLを返します。
     * 
     * @param req
     * @return requestToken.getAuthenticationURL()
     */
    public String getAuthorizeUrl(HttpServletRequest req, String callbackURL) {
        Twitter twitter = new TwitterFactory().getInstance();
        try {
            RequestToken requestToken = twitter.getOAuthRequestToken(callbackURL);
            // RequestTokenをセッションに保存しておきます。
            System.out.println("requestToken:"+requestToken.getToken());
            System.out.println("requestTokenSecret:"+requestToken.getTokenSecret());
            req.getSession().setAttribute("requestToken", requestToken.getToken());
            req.getSession().setAttribute("requestTokenSecret", requestToken.getTokenSecret());
            
            // Twitterの認証画面へのURLを返します。
            return requestToken.getAuthenticationURL();
        } catch (TwitterException e) {
            System.out.println(e.getMessage());
            return null;
        }
    }
    
    /**
     * 認証したユーザのタイムラインを返します。
     * 
     * @param req
     * @return results
     * @throws IllegalArgumentException
     */
    public TwitterList[] getUserTimeLineList(HttpServletRequest req) throws IllegalArgumentException {

        Twitter twitter = new TwitterFactory().getInstance();
        User user = (User) req.getSession().getAttribute(User.SESSION_USER_ATTR_NAME);
        TwitterUser twitterUser = null;
        if (user != null) {
            TwitterUserMeta meta = TwitterUserMeta.get();
            twitterUser = Datastore.query(meta).filter(meta.userRef.equal(user.getKey())).asSingle();

        }
        if (twitterUser == null) {
            throw new IllegalArgumentException();
        }
        twitter.setOAuthAccessToken(new AccessToken(twitterUser.getAccessToken(), twitterUser.getTokenSecret()));
        try {
            ResponseList<Status> userStatusList = twitter.getHomeTimeline();
            
            TwitterList[] results = new TwitterList[userStatusList.size()];
            for (int i = 0; i < userStatusList.size(); i++) {
                TwitterList tlist = new TwitterList();
                Status status = userStatusList.get(i);
                tlist.setText(status.getText());
                tlist.setCreatedAt(status.getCreatedAt());
                tlist.setUser(status.getUser());
                results[i] = tlist;
            }
            return results;
        } catch (TwitterException e) {
            throw new IllegalArgumentException(e);
        }
    }
    
    /**
     * 認証したユーザのタイムラインを返します。
     * 
     * @param req
     * @return results
     * @throws IllegalArgumentException
     */
    public TwitterList[] getUserTimeLineListByPage(HttpServletRequest req, int page) throws IllegalArgumentException {

        Twitter twitter = new TwitterFactory().getInstance();
        User user = (User) req.getSession().getAttribute(User.SESSION_USER_ATTR_NAME);
        TwitterUser twitterUser = null;
        if (user != null) {
            TwitterUserMeta meta = TwitterUserMeta.get();
            twitterUser = Datastore.query(meta).filter(meta.userRef.equal(user.getKey())).asSingle();

        }
        if (twitterUser == null) {
            throw new IllegalArgumentException();
        }
        twitter.setOAuthAccessToken(new AccessToken(twitterUser.getAccessToken(), twitterUser.getTokenSecret()));
        try {
            ResponseList<Status> userStatusList = twitter.getHomeTimeline(new Paging(page, 20));
            
            TwitterList[] results = new TwitterList[userStatusList.size()];
            for (int i = 0; i < userStatusList.size(); i++) {
                TwitterList tlist = new TwitterList();
                Status status = userStatusList.get(i);
                tlist.setText(status.getText());
                tlist.setCreatedAt(status.getCreatedAt());
                tlist.setUser(status.getUser());
                results[i] = tlist;
            }
            return results;
        } catch (TwitterException e) {
            throw new IllegalArgumentException(e);
        }
    }
    
    public PagableResponseList<UserList> getUserList(HttpServletRequest req) throws TwitterException {
        Twitter twitter = new TwitterFactory().getInstance();
        User user = (User) req.getSession().getAttribute(User.SESSION_USER_ATTR_NAME);
        TwitterUser twitterUser = null;
        if (user != null) {
            TwitterUserMeta meta = TwitterUserMeta.get();
            twitterUser = Datastore.query(meta).filter(meta.userRef.equal(user.getKey())).asSingle();

        }
        if (twitterUser == null) {
            throw new IllegalArgumentException();
        }
        PagableResponseList<UserList> userLists =
                twitter.getUserLists(twitterUser.getScreenName(), -1);
        return userLists;
    }
   
    public ResponseList<Status> getUsetListStatus(HttpServletRequest req, int userListId) throws TwitterException {
        Twitter twitter = new TwitterFactory().getInstance();
        User user = (User) req.getSession().getAttribute(User.SESSION_USER_ATTR_NAME);
        TwitterUser twitterUser = null;
        if (user != null) {
            TwitterUserMeta meta = TwitterUserMeta.get();
            twitterUser = Datastore.query(meta).filter(meta.userRef.equal(user.getKey())).asSingle();

        }
        if (twitterUser == null) {
            throw new IllegalArgumentException();
        }
        ResponseList<Status> userListStatuses = twitter.getUserListStatuses(userListId, new Paging());
        return userListStatuses;
    }
    
    /**
     * oauth認証後、コールバック時に呼び出されるメソッドです。
     * 
     * @param req
     * @return
     */
    public String callBack(HttpServletRequest req) {
        Twitter twitter = new TwitterFactory().getInstance();
        try {
            String requestToken = (String) req.getSession().getAttribute("requestToken");
            AccessToken accessToken = twitter.getOAuthAccessToken(requestToken);
            req.getSession().setAttribute("accessToken", accessToken);
            
            TwitterUserMeta meta = TwitterUserMeta.get();
            TwitterUser user = Datastore.query(meta).filter(meta.screenName.equal(accessToken.getScreenName())).asSingle();
            
            if(user== null){
                Key key = Datastore.allocateId(TwitterUser.class);
                user = new TwitterUser();
                user.setKey(key);
            }
            user.setAccessToken(accessToken.getToken());
            user.setAccessToken(accessToken.getTokenSecret());
            user.setScreenName(accessToken.getScreenName());
            
            Datastore.put(user);
        } catch (TwitterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return "/twt/list.jsp";
    }


    public SocialUser getAuthorizedUser(String requestToken, String ... args) throws IOException {
        Twitter twitter = new TwitterFactory().getInstance();
        TwitterUser user = null;
        try {
            AccessToken accessToken = twitter.getOAuthAccessToken(new RequestToken(requestToken, args[0]));
            TwitterUserMeta meta = TwitterUserMeta.get();
            user = Datastore.query(meta).filter(meta.screenName.equal(accessToken.getScreenName())).asSingle();
            
            if(user== null){
                Key key = Datastore.allocateId(TwitterUser.class);
                user = new TwitterUser();
                user.setKey(key);
            }
            
            user.setAccessToken(accessToken.getToken());
            user.setTokenSecret(accessToken.getTokenSecret());
            user.setScreenName(accessToken.getScreenName());

            Datastore.put(user);
        
        } catch (TwitterException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
        return user;
    }
    
    /**
     * ユーザタイムラインを以下の形式のJSONで出力します。
     *  <pre>
     *  [
     *     {
     *       "profileImageURL": "http://hogehoge.com",
     *       "screenName": "fugafuga",
     *       "text": "hoge",
     *       "createdAt": "yyyy/MM/dd hh:mm:ss"
     *     },
     *     {
     *       "profileImageURL": "http://hogehoge.com",
     *       "screenName": "fugafuga",
     *       "text": "hoge",
     *       "createdAt": "yyyy/MM/dd hh:mm:ss"
     *     }
     *  ]
     *  </pre>
     * 
     * @param res
     * @param lists
     * @throws IOException
     */
    public void writeUserTimeLineList(HttpServletResponse res, TwitterList[] lists) throws IOException {
        ServletOutputStream out = res.getOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
        writer.beginArray();
        for (TwitterList tweet : lists) {
            writer.beginObject();
            writer.name("profileImageURL").value(tweet.getProfileImageURL());
            writer.name("screenName").value(tweet.getScreenName());
            writer.name("text").value(tweet.getText());
            Date createdAt = tweet.getCreatedAt();
            DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            //df.setTimeZone(TimeZone.getTimeZone("JST"));
            String strDate = df.format(createdAt);
            writer.name("createdAt").value(strDate);
            writer.endObject();
        }
        writer.endArray();
        writer.close();
    }
    
    /**
     * ユーザリストを以下の形式のJSONで出力します。
     *  <pre>
     *  [
     *     {
     *       "id": "1000000",
     *       "name": "interesting-group",
     *     },
     *     {
     *       "id": "2000000",
     *       "name": "hoge",
     *     }
     *  ]
     *  </pre>
     * 
     * @param res
     * @param userLists
     * @throws IOException
     */
    public void writeUserList(HttpServletResponse res, PagableResponseList<UserList> userLists) throws IOException {
        ServletOutputStream out = res.getOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
        writer.beginArray();
        for (UserList list : userLists) {
            writer.beginObject();
            writer.name("id").value(list.getId());
            writer.name("name").value(list.getName());
            writer.endObject();
        }
        writer.endArray();
        writer.close();
    }

    public void writeUserList(HttpServletResponse res, ResponseList<Status> userListStatus) throws IOException {
        ServletOutputStream out = res.getOutputStream();
        JsonWriter writer = new JsonWriter(new OutputStreamWriter(out, "UTF-8"));
        writer.beginArray();
        for (Status status : userListStatus) {
            writer.beginObject();
            writer.name("profileImageURL").value(status.getUser().getProfileImageURL().toString());
            writer.name("screenName").value(status.getUser().getScreenName());
            writer.name("text").value(status.getText());
            Date createdAt = status.getCreatedAt();
            DateFormat df = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
            //df.setTimeZone(TimeZone.getTimeZone("JST"));
            String strDate = df.format(createdAt);
            writer.name("createdAt").value(strDate);
            writer.endObject();
        }
        writer.endArray();
        writer.close();
        
    }
}
